package cas;

import lombok.extern.slf4j.Slf4j;
import sun.misc.Unsafe;

import java.lang.reflect.Field;

/**
 * @author ZhuQi
 * @ClassName SpinLockDemo
 * @Date 2022/9/15 14:25
 * @Version 1.0
 * @Description 实现自旋锁
 * 条件 state > 0 表示加锁成功, state = 0 表示无锁
 */
@Slf4j
public class SpinLockDemo {

    // 需要反射一个unsafe类,jdk不允许直接获取这个对象实例
    static Unsafe unsafe = null;

    // 锁状态
    static volatile int state = 0;

    // state偏移量
    static long offset;

    // 当前持有锁的线程
    static volatile Thread ownerThread;

    // state偏移量
    static long threadOffset;

    static Object lockObject = new Object();

    static {
        try {
            Field field = Unsafe.class.getDeclaredFields()[0];
            field.setAccessible(true);
            try {
                //反射出对象
                unsafe = (Unsafe) field.get(null);
                //获取属性的偏移量
                offset = unsafe.staticFieldOffset(SpinLockDemo.class.getDeclaredField("state"));
                threadOffset = unsafe.staticFieldOffset(SpinLockDemo.class.getDeclaredField("ownerThread"));
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    /**
     * 加锁
     *
     * @return
     */
    public boolean lock() {
        if (ownerThread == null && state >= 0) {
            return doLocked();
        }
        if (!ownerThread.equals(Thread.currentThread())) {
            log.info("其他线程持有锁 threadId：{} threadName:{}"
                    , ownerThread.getId()
                    , ownerThread.getName());
            return false;
        }
        return false;
    }

    private boolean doLocked() {
        boolean locked;
        for (; ; ) {
            locked = unsafe.compareAndSwapInt(SpinLockDemo.class, offset, state, state + 1);
            if (locked) {
                boolean swapThread = unsafe.compareAndSwapObject(SpinLockDemo.class, threadOffset, null, Thread.currentThread());
                log.info("swapThread:{},locked:{}", swapThread, locked);
                return locked & swapThread;
            }
        }
    }

    /**
     * 解锁
     *
     * @return
     */
    public boolean unLock() {
        if (!Thread.currentThread().equals(ownerThread)) {
            throw new RuntimeException("非法解锁");
        }
        if (state > 0) {
            for (; ; ) {
                if (state < 0) {
                    throw new RuntimeException("解锁失败");
                }
                boolean locked = unsafe.compareAndSwapInt(SpinLockDemo.class, offset, state, state - 1);
                if (locked) {
                    if (state == 0) {
                        unsafe.compareAndSwapObject(SpinLockDemo.class, threadOffset, Thread.currentThread(), null);
                    }
                    return locked;
                }
            }
        }
        return false;
    }
}
